package com.zyf.pattern.proxy.custom;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

/**
 * 自定义代理
 * create by yifeng
 */
public class CProxy {

    public static final String ln = "\r\n";

    public static Object newProxyInstance(CClassLoader classLoader, Class<?>[] interfaces, CInvocationHandler handler) {
        // 1. 动态生成源代码.java文件
        String src = GenerateSrc(interfaces);

        // 2. java文件输出磁盘
        String filePath = CProxy.class.getResource("").getPath();
        System.out.println(filePath);
        File file = new File(filePath + "$Proxy0.java");
        try {
            FileWriter fw = new FileWriter(file);
            fw.write(src);
            fw.flush();
            fw.close();

        // 3. 生成的.java文件编译成.class文件
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager manage = compiler.getStandardFileManager(null, null, null);
        Iterable<? extends JavaFileObject> javaFileObjects = manage.getJavaFileObjects(file);

        JavaCompiler.CompilationTask task = compiler.getTask(null, manage, null, null, null, javaFileObjects);
        task.call();
        manage.close();
        // 4. 编译生成的.class文件加载到JVM中来
        Class<?> proxyClass = classLoader.findClass("$Proxy0");
        Constructor<?> constructor = proxyClass.getConstructor(CInvocationHandler.class);
        file.delete();
//             5. 返回字节码重组以后的新的代理对象
            return constructor.newInstance(handler);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    private static String GenerateSrc(Class<?>[] interfaces) {
        StringBuffer sb = new StringBuffer();
        sb.append("package com.zyf.pattern.proxy.custom;" + ln);
        sb.append("import com.zyf.pattern.proxy.jdk.Person;" + ln);
        sb.append("import  java.lang.reflect.Method;" + ln);
        sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);

        sb.append("CInvocationHandler h;" + ln);
        sb.append("public $Proxy0(CInvocationHandler h) {" + ln);
        sb.append("this.h = h;");
        sb.append("}" + ln);
        for (Method m :  interfaces[0].getMethods()) {
            sb.append("public " + m.getReturnType() + " " + m.getName() + "() { " + ln);
                sb.append("try{" + ln);
                sb.append("Method m = " + interfaces[0].getName() + ".class.getMethod(\"" + m.getName() + "\", new Class[]{});" + ln);
                sb.append("this.h.invoke(this, m, null);" + ln);
                sb.append("}catch(Throwable e) {" + ln);
                sb.append("e.printStackTrace();" + ln);
                sb.append("}");
            sb.append("}");
        }

        sb.append("}");
        return sb.toString();
    }

}
